<template>
  <el-select
    ref="selectRef"
    v-model="modelValue"
    v-bind="selectPropsComputed"
    v-on="selectListeners"
    :clear-icon="clearIcon"
    :suffix-icon="suffixIcon"
    :style="{
      width: width
    }"
  >
    <template #tag>
      <template v-if="Array.isArray(modelValue) && modelValue.length > 0">
        <template v-if="!collapseTags">
          <el-tag v-for="(item, index) in modelValue" :key="item" closable @close="removeId(item, index)">
            <span class="tag-item">{{ arrComputed[item][props.label] }}</span>
          </el-tag>
        </template>
        <!-- 多选时将选中值按文字的形式展示 -->
        <template v-else>
          <!-- 默认展示第一条选中数据 -->
          <el-tag v-for="item in modelValue.slice(0, maxCollapseTags)" :key="item"
            closable @close="removeId(modelValue[0], 0)">
            <span class="tag-item">{{ arrComputed[item][props.label] }}</span>
          </el-tag>
          <template v-if="modelValue.length > maxCollapseTags">
            <el-tag v-if="!collapseTagsTooltip">
              <span class="tag-item">+{{ modelValue.length - maxCollapseTags }}</span>
            </el-tag>
            <!-- 鼠标悬停于折叠标签的文本时，显示所有选中的标签 -->
            <el-popover v-else placement="bottom-start" width="30vw">
              <template #default>
                <div class="tag-container">
                  <el-tag
                    v-for="item in modelValue.slice(maxCollapseTags)"
                    :key="item"
                    class="popover-tag"
                    closable
                    @close="removeIdHover(item)"
                  >
                    <span>{{ arrComputed[item][props.label] }}</span>
                  </el-tag>
                </div>
              </template>
              <template #reference>
                <el-tag>
                  <span class="tag-item">+{{ modelValue.length - maxCollapseTags }}</span>
                </el-tag>
              </template>
            </el-popover>
          </template>
        </template>
      </template>
      <template v-else-if="!Array.isArray(modelValue)">
        <span class="tag-item">{{ arrComputed[modelValue][props.label] }}</span>
      </template>
    </template>
    <template #empty>
      <el-tree-v2
        ref="treeRef"
        v-bind="treePropsComputed"
        v-on="treeListeners"
        @node-click="nodeClick"
        @check="nodeChecked"
      >
        <template v-if="appendSelectAll" #default="scope">
          <div class="node-item">
            <span :title="scope.data.name" style="vertical-align: middle">
              {{ scope.data.name }}
            </span>
            <el-button
              v-if="showCheckbox"
              class="operate-btn"
              link
              type="primary"
              style="vertical-align: middle"
              @click="selectAll(scope)"
            >
              全选
            </el-button>
          </div>
        </template>
      </el-tree-v2>
    </template>
  </el-select>
</template>

<script setup lang="ts">
defineOptions({
  name: 'SelectTree',
});
import {
  SelectTreeProps,
  eventTrigger,
  TreeEvent,
  SelectEvent,
  TreeMethods,
  SelectMethods,
} from './type/useProps';
import { toArr } from '@/utils/tree';
import { CircleClose, ArrowDown } from '@element-plus/icons-vue';

const {
  width = '200px',
  // treeProps
  appendSelectAll = true,
  props = {
    label: 'label',
    value: 'id',
    children: 'children',
    disabled: 'disabled',
    class: '',
  },
  emptyText = undefined,
  highlightCurrent = true,
  expandOnClickNode = true,
  checkOnClickNode = false,
  checkOnClickLeaf = true,
  defaultExpandedKeys = undefined,
  data = [],
  checkStrictly = false,
  defaultCheckedKeys = [],
  currentNodeKey = undefined,
  filterMethod = undefined,
  indent = 16,
  icon = undefined,
  itemSize = 26,
  showCheckbox = true,
  // selectProps
  multiple = true,
  disabled = false,
  valueKey = 'value',
  size = undefined,
  clearable = false,
  collapseTags = true,
  collapseTagsTooltip = true,
  multipleLimit = 0,
  name = undefined,
  effect = 'light',
  autocomplete = 'off',
  placeholder = undefined,
  allowCreate = false,
  loading = false,
  loadingText = undefined,
  noMatchText = undefined,
  noDataText = undefined,
  popperClass = '',
  reserveKeyword = true,
  defaultFirstOption = false,
  teleported = true,
  appendTo = undefined,
  persistent = true,
  automaticDropdown = false,
  clearIcon = CircleClose,
  fitInputWidth = false,
  suffixIcon = ArrowDown,
  tagType = 'info',
  tagEffect = 'light',
  validateEvent = true,
  offset = 12,
  showArrow = true,
  placement = 'bottom-start',
  fallbackPlacements = ['bottom-start', 'top-start', 'right', 'left'],
  maxCollapseTags = 1,
  popperOptions = {},
  ariaLabel = undefined,
  emptyValues = undefined,
  valueOnClear = undefined,
  tabindex = undefined,
  closeOnCheck = false, // 选中是否隐藏下拉菜单
} = defineProps<SelectTreeProps>();
const selectListeners = reactive<any>(SelectEvent());
const treeListeners = reactive<any>(TreeEvent());
const emits = defineEmits([
  'removeTag',
  'nodeClick',
  'nodeContextmenu',
  'checkChange',
  'currentChange',
  'nodeExpand',
  'nodeCollapse',
  'nodeDragStart',
  'nodeDragEnter',
  'nodeDragLeave',
  'nodeDragOver',
  'nodeDragEnd',
  'nodeDrop',
  'filter',
  'updateKeyChildren',
  'getCheckedNodes',
  'setCheckedNodes',
  'getCheckedKeys',
  'setCheckedKeys',
  'setChecked',
  'getHalfCheckedNodes',
  'getHalfCheckedKeys',
  'getCurrentKey',
  'getCurrentNode',
  'setCurrentKey',
  'setCurrentNode',
  'getNode',
  'remove',
  'append',
  'insertBefore',
  'insertAfter',
]);
for (const key in treeListeners) {
  treeListeners[key] = eventTrigger(key, emits);
}
for (const key in selectListeners) {
  selectListeners[key] = eventTrigger(key, emits);
}
const treePropsComputed = computed(() => {
  return {
    appendSelectAll,
    props,
    emptyText,
    highlightCurrent,
    expandOnClickNode,
    checkOnClickNode,
    checkOnClickLeaf,
    defaultExpandedKeys,
    data,
    checkStrictly,
    defaultCheckedKeys,
    currentNodeKey,
    filterMethod,
    indent,
    icon,
    itemSize,
    showCheckbox,
  }
});
const selectPropsComputed = computed(() => ({
  multiple,
  disabled,
  valueKey,
  size,
  clearable,
  collapseTags,
  collapseTagsTooltip,
  multipleLimit,
  name,
  effect,
  autocomplete,
  placeholder,
  allowCreate,
  loading,
  loadingText,
  noMatchText,
  noDataText,
  popperClass,
  reserveKeyword,
  defaultFirstOption,
  teleported,
  appendTo,
  persistent,
  automaticDropdown,
  fitInputWidth,
  tagType,
  tagEffect,
  validateEvent,
  offset,
  showArrow,
  placement,
  fallbackPlacements,
  maxCollapseTags,
  popperOptions,
  ariaLabel,
  emptyValues,
  valueOnClear,
  tabindex,
}));

const modelValue = defineModel<any[] | string>('modelValue', {
  required: true,
  default: () => [],
});

const arrComputed = computed(() => {
  const dataArr = toArr(data);
  const ret: any = {};
  dataArr.forEach((v: any) => {
    ret[v[props.value]] = v;
  });
  return ret;
});
const treeRef = ref<any>();
const selectRef = ref<any>();
// modelValue处理
let modelValueMap: any = {};

const nodeChecked = (data: any, info: any) => {
  console.log('data-info', data, info);
  const { checkedNodes } = info;
  const checkedKeys = checkedNodes.map((item: any) => item[props.value]);
  modelValue.value = checkedKeys;
};
// 先执行父中 v-on 的 nodeClick 事件(如果有的话)，再执行子的 nodeClick
// data: 节点点击的节点对象，node: TreeNode 的 node 属性, TreeNode和事件对象
const nodeClick = (data: any, node: any) => {
  console.log('子nodeClick-', data, node);
  if(closeOnCheck && !showCheckbox) {
    modelValue.value = data[props.value];
    selectRef.value.blur();
  }
};

watch(modelValue, (newVal: any) => {
  modelValueMap = {};
    if (treeRef.value) {
      treeRef.value.setCheckedKeys(newVal);
      if (Array.isArray(newVal) && newVal.length !== 0) {
        newVal.forEach((item: any) => {
          modelValueMap[item] = true;
        });
      } else {
        modelValueMap[newVal] = true;
      }
    }
  },
  { deep: true }
);

// watch(() => data, (newVal: any) => {
//   console.log('tree-', newVal);
//   treeRef.value?.setData(newVal);
// }, {
//   deep: true,
//   immediate: true
// })

// 工具方法
function selectAll(scope: any) {
  console.log('scope-', scope, 'modelValue-', modelValue);
  const data = scope.data;
  const checkNodes: any = [];
  const checkNodesMap: any = {};
  handleSelect(data);
  function handleSelect(node: any) {
    checkNodes.push(node.id);
    checkNodesMap[node.id] = node;
    if (node.children) {
      node.children.forEach((child: any) => {
        handleSelect(child);
      });
    }
  }
  let finalIds: any = [...modelValue.value];
  if (modelValueMap[data.id]) {
    finalIds = finalIds.filter((item: any) => !checkNodesMap[item]);
  } else {
    finalIds = Array.from(new Set(finalIds.concat(checkNodes)));
  }
  treeRef.value.setCheckedKeys(finalIds);
  modelValue.value = finalIds;
}

const removeId = (id: string, index?: number, hover?: boolean) => {
  if(Array.isArray(modelValue.value)){
    modelValue.value.splice(index, 1);
  }
  emits('removeTag', id);
};

const removeIdHover = (id: string) => {
  let index = modelValue.value.indexOf(id);
  modelValue.value.splice(index, 1);
}

const expose: any = {
  ...TreeMethods(),
  ...SelectMethods(),
};
for (const key in expose) {
  expose[key] = function () {
    const currentMethods = treeRef.value[key] || selectRef.value[key];
    if (!currentMethods) {
      console.error(`方法${key}不存在`);
      return;
    }
    return currentMethods(...arguments);
  };
}

defineExpose(expose);
</script>

<style lang="scss" scoped>
.tag-item{
  margin-right: 5px;
}
.tag-container{
  height: 400px;
  overflow: auto;
}
.popover-tag{
  margin-right: 10px;
  margin-bottom: 10px;
}
.node-item {
  width: 100%;
  position: relative;
  box-sizing: border-box;
  text-align: left;
  .operate-btn {
    margin-left: 10px;
  }
}
</style>
